package wsi.depend;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

import org.rygh.core.util.UIo;

import wsi.depend.model.DependencyEdge;
import wsi.depend.model.DependencyGraph;
import wsi.depend.model.DependencyNode;
import wsi.exp.Exp2TreeToEntity;
import wsi.exp.ExpFolderController;
import wsi.function.Function;
import wsi.function.FunctionController;
import wsi.ws.WebServices;

public class DependencyController {

	private final DependencyGraph graph = new DependencyGraph();
	private final WebServices service;

	public DependencyController(WebServices service) {
		this.service = service;
	}

	/**
	 * dependant: getArtistById, provider: getArtistByName
	 * 
	 * @param dependant
	 * @return
	 */
	private boolean attach(Function dependant) {
		// if (!dependant.getName().equals("getPublisherById"))
		// return false;

		DependencyDetector detector = new DependencyDetector(dependant);
		List<DependencyNode> list = new LinkedList<>();

		DependencyNode dep_node;
		DependencyEdge prov_edge;
		Function prov_func;
		boolean attached = false;

		for (DependencyNode prov_node : graph.getNodes()) {
			prov_func = prov_node.getValue();

			// if (!prov_func.getName().equals("getPublisherByName"))
			// continue;

			if (prov_func == dependant)
				continue;

			if (graph.hasIncomingNeighbor(prov_node, dependant))
				continue;

			detector.reset();
			detector.setProvider(prov_func);

			if (!detector.detect())
				continue;

			prov_edge = new DependencyEdge(prov_node);
			prov_edge.setValue(detector.getDependency());

			dep_node = graph.findNodeByValue(dependant);

			if (dep_node == null) {
				/*
				 * there are cases for one loop we add twice the same dependant
				 * node. thus we need to check list.
				 */
				for (DependencyNode n : list)
					if (n.getValue() == dependant)
						dep_node = n;

				if (dep_node == null) {
					dep_node = new DependencyNode(dependant);
					list.add(dep_node);
				}
			}

			dep_node.addEdge(prov_edge);
			attached = true;

			System.out.printf("[info] new attachment %s -> %s\n",
					dependant.getName(), prov_func.getName());
		}

		for (DependencyNode n : list) {
			System.out.printf("[info] new node %s\n", n.getValue().getName());
			graph.addNode(n);
		}

		return attached;
	}

	public void compute() {
		System.out.printf("[info] compute depend for %s\n", service.name());

		FunctionController fctrl = FunctionController.shared();

		List<Function> pool = new LinkedList<>();
		List<Function> attached = new LinkedList<>();

		for (Function function : fctrl.getFunctions(service.name())) {
			if (!function.isDirect()) {
				pool.add(function);
				continue;
			}

			if (!Exp2TreeToEntity.isFunctionCorrectlyDetected(function))
				continue;

			graph.addNode(new DependencyNode(function));
		}

		System.out.println("Initial graph:\n" + graph);

		boolean changed = !pool.isEmpty();
		int c = 0;

		while (changed) {
			changed = false;

			for (Function f : pool)
				if (attach(f)) {
					attached.add(f);
					changed = true;
				}

			attached.clear();

			c++;
			save();

			System.out.printf("[info] so far %d runs.\ngraph:\n%s\n", c,
					graph.toString());

			// break;
		}

		save();
	}

	public void save() {
		File dir = ExpFolderController.shared().getFolderForExp6();

		File file = new File(dir, String.format("depend_%s.xml", service));
		UIo.saveFile(file, graph.toXmlString());
	}
}
